<template>
    <canvas ref="canvas" @mousedown="mouseDown" @mousemove="mouseMove" @mouseup="mouseUp"
    @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd"></canvas>
  </template>
  
  <script>
  export default {
    props: {
      width: {
        type: Number,
        default: 800
      },
      height: {
        type: Number,
        default: 300
      },
      lineWidth: {
        type: Number,
        default: 4
      },
      lineColor: {
        type: String,
        default: '#000000'
      },
      bgColor: {
        type: String,
        default: ''
      },
      isCrop: {
        type: Boolean,
        default: false
      },
      isClearBgColor: {
        type: Boolean,
        default: true
      },
      format: {
        type: String,
        default: 'image/png'
      },
      quality: {
        type: Number,
        default: 1
      }
    },
    data () {
      return {
        hasDrew: false,
        resultImg: '',
        points: [],
        canvasTxt: null,
        startX: 0,
        startY: 0,
        isDrawing: false,
        sratio: 1
      }
    },
    computed: {
      ratio () {
        return this.height / this.width
      },
      stageInfo () {
        return this.$refs.canvas.getBoundingClientRect()
      },
      myBg () {
        return this.bgColor ? this.bgColor : 'rgba(255, 255, 255, 0)'
      }
    },
    watch: {
      'myBg': function (newVal) {
        this.$refs.canvas.style.background = newVal
      }
    },
    beforeMount () {
      window.addEventListener('resize', this.$_resizeHandler)
    },
    beforeDestroy () {
      window.removeEventListener('resize', this.$_resizeHandler)
    },
    mounted () {
      const canvas = this.$refs.canvas
      canvas.height = this.height
      canvas.width = this.width
      canvas.style.background = this.myBg
      this.$_resizeHandler()
      // 在画板以外松开鼠标后冻结画笔
      document.onmouseup = () => {
        this.isDrawing = false
      }
    },
    methods: {
      $_resizeHandler () {
        const canvas = this.$refs.canvas
        canvas.style.width = this.width + "px"
        const realw = parseFloat(window.getComputedStyle(canvas).width)
        canvas.style.height = this.ratio * realw + "px";
        this.canvasTxt = canvas.getContext('2d')
        this.canvasTxt.scale(1 * this.sratio, 1 * this.sratio)
        this.sratio = realw / this.width
        this.canvasTxt.scale(1 / this.sratio, 1 / this.sratio)
      },
      // pc
      mouseDown (e) {
        e = e || event
        e.preventDefault()
        this.isDrawing = true
        this.hasDrew = true
        let obj = {
          x: e.offsetX,
          y: e.offsetY
        }
        this.drawStart(obj)
      },
      mouseMove (e) {
        e = e || event
        e.preventDefault()
        if (this.isDrawing) {
          let obj = {
            x: e.offsetX,
            y: e.offsetY
          }
          this.drawMove(obj)
        }
      },
      mouseUp (e) {
        e = e || event
        e.preventDefault()
        let obj = {
          x: e.offsetX,
          y: e.offsetY
        }
        this.drawEnd(obj)
        this.isDrawing = false
      },
      // mobile
      touchStart (e) {
        e = e || event
        e.preventDefault()
        this.hasDrew = true
        if (e.touches.length === 1) {
          let obj = {
            x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
            y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top
          }
          this.drawStart(obj)
        }
      },
      touchMove (e) {
        e = e || event
        e.preventDefault()
        if (e.touches.length === 1) {
          let obj = {
            x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
            y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top
          }
          this.drawMove(obj)
        }
      },
      touchEnd (e) {
        e = e || event
        e.preventDefault()
        if (e.touches.length === 1) {
          let obj = {
            x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
            y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top
          }
          this.drawEnd(obj)
        }
      },
      // 绘制
      drawStart (obj) {
        this.startX = obj.x
        this.startY = obj.y
        this.canvasTxt.beginPath()
        this.canvasTxt.moveTo(this.startX, this.startY)
        this.canvasTxt.lineTo(obj.x, obj.y)
        this.canvasTxt.lineCap = 'round'
        this.canvasTxt.lineJoin = 'round'
        this.canvasTxt.lineWidth = this.lineWidth * this.sratio
        this.canvasTxt.stroke()
        this.canvasTxt.closePath()
        this.points.push(obj)
      },
      drawMove (obj) {
        this.canvasTxt.beginPath()
        this.canvasTxt.moveTo(this.startX, this.startY)
        this.canvasTxt.lineTo(obj.x, obj.y)
        this.canvasTxt.strokeStyle = this.lineColor
        this.canvasTxt.lineWidth = this.lineWidth * this.sratio
        this.canvasTxt.lineCap = 'round'
        this.canvasTxt.lineJoin = 'round'
        this.canvasTxt.stroke()
        this.canvasTxt.closePath()
        this.startY = obj.y
        this.startX = obj.x
        this.points.push(obj)
      },
      drawEnd (obj) {
        this.canvasTxt.beginPath()
        this.canvasTxt.moveTo(this.startX, this.startY)
        this.canvasTxt.lineTo(obj.x, obj.y)
        this.canvasTxt.lineCap = 'round'
        this.canvasTxt.lineJoin = 'round'
        this.canvasTxt.stroke()
        this.canvasTxt.closePath()
        this.points.push(obj)
        this.points.push({x: -1, y: -1})
      },
      // 操作
      generate (options) {
        let imgFormat = options && options.format ? options.format: this.format
        let imgQuality = options && options.quality ? options.quality: this.quality
        const pm =  new Promise((resolve, reject) => {
          if (!this.hasDrew) {
            reject(`Warning: Not Signned!`)
            return
          }
          var resImgData = this.canvasTxt.getImageData(0, 0, this.$refs.canvas.width, this.$refs.canvas.height)
          this.canvasTxt.globalCompositeOperation = "destination-over"
          this.canvasTxt.fillStyle = this.myBg
          this.canvasTxt.fillRect(0,0,this.$refs.canvas.width ,this.$refs.canvas.height)
          this.resultImg = this.$refs.canvas.toDataURL(imgFormat, imgQuality)
          var resultImg = this.resultImg
          this.canvasTxt.clearRect(0, 0, this.$refs.canvas.width ,this.$refs.canvas.height)
          this.canvasTxt.putImageData(resImgData, 0, 0)
          this.canvasTxt.globalCompositeOperation = "source-over"
          if (this.isCrop) {
            const crop_area = this.getCropArea(resImgData.data)
            var crop_canvas = document.createElement('canvas')
            const crop_ctx = crop_canvas.getContext('2d')
            crop_canvas.width = crop_area[2] - crop_area[0]
            crop_canvas.height = crop_area[3] - crop_area[1]
            const crop_imgData = this.canvasTxt.getImageData(...crop_area)
            crop_ctx.globalCompositeOperation = "destination-over"
            crop_ctx.putImageData(crop_imgData, 0, 0)
            crop_ctx.fillStyle = this.myBg
            crop_ctx.fillRect(0, 0, crop_canvas.width , crop_canvas.height)
            resultImg = crop_canvas.toDataURL(imgFormat, imgQuality)
            crop_canvas = null
          }
          resolve(resultImg)
        })
        return pm
      },
      reset () {
        this.canvasTxt.clearRect(
          0,
          0,
          this.$refs.canvas.width,
          this.$refs.canvas.height
        )
        if (this.isClearBgColor) {
          this.$emit('update:bgColor', '')
          this.$refs.canvas.style.background = 'rgba(255, 255, 255, 0)'
        }
        this.points = []
        this.hasDrew = false
        this.resultImg = ''
      },
      getCropArea (imgData) {
        var topX = this.$refs.canvas.width; var btmX = 0; var topY = this.$refs.canvas.height; var btnY = 0
        for (var i = 0; i < this.$refs.canvas.width; i++) {
          for (var j = 0; j < this.$refs.canvas.height; j++) {
            var pos = (i + this.$refs.canvas.width * j) * 4
            if (imgData[pos] > 0 || imgData[pos + 1] > 0 || imgData[pos + 2] || imgData[pos + 3] > 0) {
              btnY = Math.max(j, btnY)
              btmX = Math.max(i, btmX)
              topY = Math.min(j, topY)
              topX = Math.min(i, topX)
            }
          }
        }
        topX++
        btmX++
        topY++
        btnY++
        const data = [topX, topY, btmX, btnY]
        return data
      }
    }
  }
  </script>
  
  <style scoped>
  canvas {
    max-width: 100%;
    display: block;
  }
  </style>